#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author: bao
# @Date:   2017-01-16 11:59:29
# @Last Modified by:   bao
# @Last Modified time: 2017-07-26 16:27:26

import heapq

Logging = 0
FlowArrival = 1
FlowFinished = 2
FlowSourceFree = 3
FlowDestFree = 4
LinkAdjusting = 5
LinkAdjusted = 6
UpdateTopology = 7
Sample = 8
UpdatedTopology = 9


class Event(object):
    """Event"""

    def __init__(self, time, eventType, cancelled=False):
        self.time = time
        self.type = eventType
        self.cancelled = cancelled

    def __repr__(self):
        if self.type == FlowArrival:
            return "event:{!r}, FlowArrival, cancelled:{!r}---------".format(self.time, self.cancelled)
        elif self.type == FlowFinished:
            return "event:{!r}, FlowFinished, cancelled:{!r}---------".format(self.time, self.cancelled)
        else:
            return "event:{!r}, type:{!r}, cancelled:{!r}--------".format(self.time, self.type, self.cancelled)


class FlowArrivalEvent(Event):
    """A flow arrival event"""

    def __init__(self, time, flow):
        Event.__init__(self, time, FlowArrival)
        self.flow = flow

    def process_event(self, scheduler, eventQueue):
        # Update related flow's transfer time in inserting a flow
        scheduler.UpdateFlow(self.flow, FlowArrival, eventQueue, self.time)


class FlowFinishedEvent(Event):
    """A flow finished FlowFinishedEvent"""

    def __init__(self, time, flow):
        Event.__init__(self, time, FlowFinished)
        self.flow = flow

    def process_event(self, scheduler, eventQueue):
        # Update related flow's transfer time in removing a flow
        scheduler.UpdateFlow(self.flow, FlowFinished, eventQueue, self.time)


class FlowSourceFreeEvent(Event):
    """A flow finished FlowFinishedEvent"""

    def __init__(self, time, flow):
        Event.__init__(self, time, FlowSourceFree)
        self.flow = flow

    def process_event(self, scheduler, eventQueue):
        scheduler.UpdateFlow(self.flow, FlowSourceFree, eventQueue, self.time)


class FlowDestFreeEvent(Event):
    """A flow finished FlowFinishedEvent"""

    def __init__(self, time, flow):
        Event.__init__(self, time, FlowDestFree)
        self.flow = flow

    def process_event(self, scheduler, eventQueue):
        scheduler.UpdateFlow(self.flow, FlowDestFree, eventQueue, self.time)


class UpdateTopologyEvent(Event):
    """docstring for ReconfigureEvent"""

    def __init__(self, time):
        Event.__init__(self, time, UpdateTopology)

    def process_event(self, scheduler, eventQueue):
        scheduler.UpdateTopology(eventQueue, self.time)


class SampleEvent(Event):
    """Periodically sample the network bandwidth"""

    def __init__(self, time, sample_interval, sample_file):
        Event.__init__(self, time, Sample)
        self.sample_interval = sample_interval
        self.sample_file = sample_file

    def process_event(self, scheduler, eventQueue):
        event = SampleEvent(self.time + self.sample_interval, self.sample_interval, self.sample_file)
        eventQueue.push(event, event.time)
        bandwidth = 0
        for flow in scheduler.runningFlows:
            bandwidth += flow.bw
        print >> self.sample_file, "time", self.time, "bandwidth", bandwidth


class LinkAdjustingEvent(Event):
    """A flow finished FlowFinishedEvent"""

    def __init__(self, time):
        Event.__init__(self, time, LinkAdjusting)

    def process_event(self, scheduler, eventQueue):
        # links are being adjusted
        # Update the network (including topology, routing and flow bandwidth based on intermediate topology)
        scheduler.UpdateNetwork(eventQueue, self.time)


class LinkAdjustedEvent(Event):
    """network topology has been adjusted"""

    def __init__(self, time):
        Event.__init__(self, time, LinkAdjusted)

    def process_event(self, scheduler, eventQueue):
        # links has been adjusted
        # Update the network (including topology, routing and flow bandwidth based on new topology)
        scheduler.UpdateTopologyFinished(eventQueue, self.time)


class LogEvent(Event):
    """docstring for LogEvent"""

    def __init__(self, time):
        Event.__init__(self, time, Logging)

    def process_event(self, scheduler, eventQueue):
        scheduler.UpdateNetwork(eventQueue, self.time)


class EventQueue(object):
    """priority event queue"""

    def __init__(self):
        self._queue = []
        self._index = 0

    def push(self, item, priority):
        heapq.heappush(self._queue, (priority, self._index, item))
        self._index += 1

    def pop(self):
        return heapq.heappop(self._queue)[-1]

    @property
    def queue(self):
        return self._queue
